package com.stuffthathappens.concurrency;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.Random;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;

/**
 * @author Eric Burke
 */
public class DemoCyclicBarrier extends JFrame {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new DemoCyclicBarrier().setVisible(true);
            }
        });
    }

    public DemoCyclicBarrier() {
        super("CyclicBarrier Demonstration");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        Container cp = getContentPane();
        cp.setLayout(new GridLayout(0, 1));

        Random rand = new Random();

        int numParties = 5;
        CyclicBarrier barrier = new CyclicBarrier(numParties);
        for (int i=0; i<numParties; i++) {
            cp.add(new PartyPanel(barrier, rand.nextInt(100)));
        }

        pack();
        setLocationRelativeTo(null);
    }



    private class PartyPanel extends JPanel {
        private final CyclicBarrier barrier;
        private JProgressBar progressBar = new JProgressBar(0, 100);
        private final int maxDelayMs;

        private Action startAction = new AbstractAction("Start") {
            public void actionPerformed(ActionEvent e) {
                startClicked();
            }
        };

        public PartyPanel(CyclicBarrier barrier, int maxDelayMs) {
            setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
            this.barrier = barrier;
            this.maxDelayMs = maxDelayMs;

            add(Box.createHorizontalStrut(5));
            add(new JButton(startAction));
            add(Box.createHorizontalStrut(5));
            add(progressBar);
            add(Box.createHorizontalStrut(5));
        }

        private void startClicked() {
            startAction.setEnabled(false);
            progressBar.setValue(0);
            progressBar.setIndeterminate(true);

            SwingWorker<Object, Object> worker = new SwingWorker<Object, Object>() {
                protected Object doInBackground() throws Exception {

                    Random rand = new Random();

                    //
                    // Block until the other threads all start!
                    //
                    barrier.await();

                    for (int i=0; i<100; i++) {
                        setProgress(i);
                        TimeUnit.MILLISECONDS.sleep(rand.nextInt(maxDelayMs));
                    }

                    return null;
                }

                protected void done() {
                    startAction.setEnabled(true);
                    progressBar.setValue(100);
                }
            };

            worker.addPropertyChangeListener(new PropertyChangeListener() {
                public void propertyChange(PropertyChangeEvent evt) {
                    if ("progress".equals(evt.getPropertyName())) {
                        progressBar.setIndeterminate(false);
                        progressBar.setValue((Integer) evt.getNewValue());
                    }
                }
            });

            worker.execute();
        }

    }
}
